package com.biz.primus.ms.homepage.helper;

import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;

@SuppressWarnings("rawtypes")
public class SimpleCacheHelper {

    private static final long CACHE_MAX_SIZE = 5000;

	private static final Map<Class, Map<String, LoadingCache<Object, Object>>> loadingCacheMap = new ConcurrentHashMap<>();

    public static void clearCache(Class cacheFor, String cacheLoaderKey){

        Map<String, LoadingCache<Object, Object>> stringLoadingCacheMap = loadingCacheMap.get(cacheFor);
        if(stringLoadingCacheMap != null){
            LoadingCache<Object, Object> loadingCache = stringLoadingCacheMap.get(cacheLoaderKey);
            if(loadingCache != null){
                loadingCache.invalidateAll();
            }
        }
    }

    public static void clearCache(Class cacheFor, String cacheLoaderKey, String... dataKeys){

        Map<String, LoadingCache<Object, Object>> stringLoadingCacheMap = loadingCacheMap.get(cacheFor);
        if(stringLoadingCacheMap != null){
            LoadingCache<Object, Object> loadingCache = stringLoadingCacheMap.get(cacheLoaderKey);
            if(loadingCache != null){
                for (String dataKey : dataKeys) {
                    loadingCache.invalidate(dataKey);
                }

            }
        }
    }

    @SuppressWarnings("unchecked")
    public static <K, V> V loadFromCacheOrCacheLoader(Class cacheFor, String cacheLoaderKey, CacheLoader<K , V> cacheLoader, K dataKey,
                                                      TimeUnit cacheTimeUnit, long cacheDuration){
        if (cacheFor == null){
            throw new RuntimeException("Argument cacheFor is null.");
        }
        Map<String, LoadingCache<Object, Object>> serviceLoadingCacheMap =
            loadingCacheMap.get(cacheFor);
        if(serviceLoadingCacheMap == null) {
            synchronized (cacheFor){
                serviceLoadingCacheMap = loadingCacheMap.get(cacheFor);
                if(serviceLoadingCacheMap == null){
                    serviceLoadingCacheMap = new ConcurrentHashMap<>();
                    loadingCacheMap.put(cacheFor, serviceLoadingCacheMap);
                }
            }
        }
        LoadingCache<Object, Object> loadingCache = serviceLoadingCacheMap.get(cacheLoaderKey);
        if (loadingCache == null) {
            synchronized (cacheFor) {
                loadingCache = serviceLoadingCacheMap.get(cacheLoaderKey);
                if (loadingCache == null) {
                    loadingCache = (LoadingCache<Object, Object>) CacheBuilder.newBuilder().maximumSize(CACHE_MAX_SIZE)
                      .expireAfterWrite(cacheDuration, cacheTimeUnit).build(cacheLoader);
                    serviceLoadingCacheMap.put(cacheLoaderKey, loadingCache);
                }
            }
        }
        try {
            return (V) loadingCache.get(dataKey);
        } catch (ExecutionException | CacheLoader.InvalidCacheLoadException e) {
            return null;
        }
    }

}
